/**
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { Ref } from 'vue';
import {
    CellMode,
    UseCellPosition,
    UseColumn,
    UseEdit,
    UseGroupData,
    UseRow,
    UseVirtualScroll,
    UseVisualData,
    VisualData,
    VisualDataCell
} from '../../composition/types';
import { DataGridColumn, DataGridProps } from '../../data-grid.props';

export default function (
    props: DataGridProps,
    primaryGridContentRef: Ref<any>,
    leftFixedGridContentRef: Ref<any>,
    rightFixedGridContentRef: Ref<any>,
    useCellPositionComposition: UseCellPosition,
    useColumnComposition: UseColumn,
    useEditComposition: UseEdit,
    useGroupDataComposition: UseGroupData,
    useRowComposition: UseRow,
    useVisualDataComposition: UseVisualData,
    useVirtualScrollComposition: UseVirtualScroll,
    visibleDatas: Ref<VisualData[]>
) {
    const {
        calculateCellPositionInRow,
        cellKey,
        cellPosition,
        rowKey,
        rowPosition,
        rowSpanCellPosition,
        groupCellPosition,
        summaryCellPosition
    } = useCellPositionComposition;

    const { collpaseGroupIconClass } = useGroupDataComposition;

    const { gridRowClass, onClickRow, onMouseoverRow } = useRowComposition;

    const { onClickCell } = useEditComposition;

    const { toggleGroupRow } = useVisualDataComposition;

    const { columnContext, hasLeftFixedColumn, hasRightFixedColumn } = useColumnComposition;

    const {
        gridDataStyle,
        gridMergedDataStyle,
        leftFixedGridDataStyle,
        leftFixedGridMergedDataStyle,
        rightFixedGridDataStyle,
        rightFixedGridMergedDataStyle,
    } = useVirtualScrollComposition;

    function onToggleGroupRow($event: MouseEvent, dataItem: VisualData) {
        dataItem.collapse = !dataItem.collapse;
        visibleDatas.value = toggleGroupRow(dataItem.collapse ? 'collapse' : 'expand', dataItem, visibleDatas.value);
    }

    function renderGroupRowCells(groupRow: VisualData, cellPositionMap: Record<string, { left: number; width: number }>) {
        const groupRowCells = [];
        if (groupRow.groupField && cellPositionMap[groupRow.groupField]) {
            for (let layer = 0; layer <= groupRow.layer; layer++) {
                groupRowCells.push(
                    <div class="fv-grid-group-row-icon" onClick={(payload: MouseEvent) => onToggleGroupRow(payload, groupRow)}>
                        <span class={collpaseGroupIconClass(groupRow)}></span>
                    </div>
                );
            }
            const groupCell = groupRow.data[groupRow.groupField];
            groupRowCells.push(
                <div
                    ref={groupCell.setRef}
                    key={cellKey(groupRow, groupRow.layer + 1)}
                    class="fv-grid-cell"
                    style={groupCellPosition(groupCell, cellPositionMap, groupRow.layer)}>
                    {groupCell.data}
                </div>
            );
            Object.values(groupRow.data)
                .filter((cell: VisualDataCell) => cell.field !== groupRow.groupField && cell.colSpan !== 0)
                .forEach((cell: VisualDataCell) => {
                    groupRowCells.push(
                        <div
                            ref={cell.setRef}
                            key={cellKey(groupRow, cell.index)}
                            class="fv-grid-cell"
                            style={cellPosition(cell, cellPositionMap)}>
                            {cell.data}
                        </div>
                    );
                });
        }
        return groupRowCells;
    }

    function renderGroupRow(groupRow: VisualData, cellPositionMap: Record<string, { left: number; width: number }>) {
        return (
            groupRow.layer > -1 && (
                <div
                    ref={groupRow.setRef}
                    key={rowKey(groupRow)}
                    class={gridRowClass(groupRow)}
                    style={rowPosition(groupRow)}
                    onClick={(payload: MouseEvent) => onClickRow(payload, groupRow)}
                    onMouseover={(payload: MouseEvent) => onMouseoverRow(payload, groupRow)}>
                    {renderGroupRowCells(groupRow, cellPositionMap)}
                </div>
            )
        );
    }

    function renderSummaryRowCells(summaryRow: VisualData, cellPositionMap: Record<string, { left: number; width: number }>) {
        const summaryRowCells = [];
        if (summaryRow.groupField && cellPositionMap[summaryRow.groupField]) {
            const summaryCell = summaryRow.data[summaryRow.groupField];
            summaryRowCells.push(
                <div
                    ref={summaryCell.setRef}
                    key={cellKey(summaryRow, summaryRow.layer + 1)}
                    class="fv-grid-cell"
                    style={summaryCellPosition(summaryCell, cellPositionMap, summaryRow.layer)}>
                    {summaryCell.data}
                </div>
            );
        }
        return summaryRowCells;
    }

    function renderSummaryRow(summaryRow: VisualData, cellPositionMap: Record<string, { left: number; width: number }>) {
        return (
            summaryRow.layer > -1 && (
                <div
                    ref={summaryRow.setRef}
                    key={rowKey(summaryRow)}
                    class={gridRowClass(summaryRow)}
                    style={rowPosition(summaryRow)}
                    onClick={(payload: MouseEvent) => onClickRow(payload, summaryRow)}
                    onMouseover={(payload: MouseEvent) => onMouseoverRow(payload, summaryRow)}>
                    {renderSummaryRowCells(summaryRow, cellPositionMap)}
                </div>
            )
        );
    }

    function renderDataRow(visualDataRow: VisualData, cellPositionMap: Record<string, { left: number; width: number }>) {
        return (
            <div
                ref={visualDataRow.setRef}
                key={rowKey(visualDataRow)}
                class={gridRowClass(visualDataRow)}
                style={rowPosition(visualDataRow)}
                onClick={(payload: MouseEvent) => onClickRow(payload, visualDataRow)}
                onMouseover={(payload: MouseEvent) => onMouseoverRow(payload, visualDataRow)}>
                {Object.values(visualDataRow.data)
                    .filter((cell: VisualDataCell) => cellPositionMap[cell.field] && cell.rowSpan === 1)
                    .map((cell: VisualDataCell) => {
                        return (
                            <div
                                ref={cell.setRef}
                                key={cellKey(visualDataRow, cell.index)}
                                class="fv-grid-cell"
                                style={cellPosition(cell, cellPositionMap)}
                                onClick={(payload: MouseEvent) => onClickCell(payload, cell)}>
                                {
                                    cell.mode === CellMode.editing ?
                                        cell.getEditor(cell) :
                                        (
                                            cell.formatter ?
                                                cell.formatter(cell, visualDataRow) :
                                                (cell.data != null ? cell.data.toString() : cell.data)
                                        )
                                }
                            </div>
                        );
                    })}
            </div>
        );
    }

    const visualDataRowRenders = [renderDataRow, renderGroupRow, renderSummaryRow];

    function renderGridData(columns: DataGridColumn[], area: 'left' | 'primary' | 'right') {
        const cellPositionMap = calculateCellPositionInRow(columns);
        return visibleDatas.value.map((visualDataRow: VisualData) => {
            return visualDataRowRenders[visualDataRow.type](visualDataRow, cellPositionMap);
        });
    }

    function renderMergedGridData(columns: DataGridColumn[]) {
        const cellPositionMap = calculateCellPositionInRow(columns);
        return visibleDatas.value.map((visualDataRow: VisualData) => {
            return Object.values(visualDataRow.data)
                .filter((cell) => cellPositionMap[cell.field] && cell.rowSpan > 1)
                .map((cell) => {
                    return (
                        <div
                            key={cellKey(visualDataRow, cell.index)}
                            class="fv-grid-cell fv-grid-merged-cell"
                            style={rowSpanCellPosition(visualDataRow, cell, cellPositionMap)}>
                            {cell.data}
                        </div>
                    );
                });
        });
    }

    function renderLeftFixedGrid() {
        return (
            <div ref={leftFixedGridContentRef} class="fv-grid-content-left-fixed">
                <div class="fv-grid-data" style={leftFixedGridDataStyle.value}>
                    {renderGridData(columnContext.value.leftColumns.filter((column: DataGridColumn) => column.visible), 'left')}
                </div>
                <div class="fv-grid-merge-date" style={leftFixedGridMergedDataStyle.value}>
                    {renderMergedGridData(columnContext.value.leftColumns.filter((column: DataGridColumn) => column.visible))}
                </div>
            </div>
        );
    }

    function renderPrimaryFixedDataArea() {
        return (
            <div ref={primaryGridContentRef} class="fv-grid-content-primary">
                <div class="fv-grid-data" style={gridDataStyle.value}>
                    {renderGridData(columnContext.value.primaryColumns.filter((column: DataGridColumn) => column.visible), 'primary')}
                </div>
                <div class="fv-grid-merge-date" style={gridMergedDataStyle.value}>
                    {renderMergedGridData(columnContext.value.primaryColumns.filter((column: DataGridColumn) => column.visible))}
                </div>
            </div>
        );
    }

    function renderRightFixedGrid() {
        return (
            <div ref={rightFixedGridContentRef} class="fv-grid-content-right-fixed">
                <div class="fv-grid-data" style={rightFixedGridDataStyle.value}>
                    {renderGridData(columnContext.value.rightColumns.filter((column: DataGridColumn) => column.visible), 'right')}
                </div>
                <div class="fv-grid-merge-date" style={rightFixedGridMergedDataStyle.value}>
                    {renderMergedGridData(columnContext.value.rightColumns.filter((column: DataGridColumn) => column.visible))}
                </div>
            </div>
        );
    }

    function renderDataArea() {
        const renderResult = [];
        if (hasLeftFixedColumn.value) {
            renderResult.push(renderLeftFixedGrid());
        }
        renderResult.push(renderPrimaryFixedDataArea());
        if (hasRightFixedColumn.value) {
            renderResult.push(renderRightFixedGrid());
        }
        return renderResult;
    }

    return { renderDataArea };
}
